<?php
// +----------------------------
// | Created by PhpStorm
// +----------------------------
// | Author: YnnSon
// +----------------------------
namespace app\api\service;

use app\lib\exception\ParameterException;
use nusoap_client;
use think\Config;

class Car
{
    /**
     * 接口地址
     */
    private $url;
    /**
     * 备案编号
     * string(10)
     * 是否必填:是
     * 备注:提交备案后下发
     */
    private $babh;
    /**
     * 随机数
     * string(32)
     * 是否必填:是
     * 备注:不可重复
     */
    private $nonce;
    /**
     * 时间戳
     * string(10)
     * 是否必填:是
     * 备注:time()
     */
    private $timestamp;
    /**
     * 调用接口的ID
     * string(5)
     * 是否必填:是
     * 备注:12C05,12C06,12C01,12C03,12C02
     */
    private $action;
    /**
     * 签名
     * string(500)
     * 是否必填:是
     * 签名项:babh,nonce,timestamp,action,appsecret
     */
    private $sign;
    /**
     * 密文
     * string(4000)
     * 是否必填:是
     * 签名项:接口对应数据项转换为JSON字符串后使用RSA加密
     */
    private $data;
    /**
     * 秘钥
     * 备注:备案后下发
     */
    private $rsaPrivateKeyFilePath;
    private $rsaPublicKeyFilePath;

    public function __construct($param)
    {
        $config = Config::get('subscribe');
        $this->url = $config['url'];
        $this->babh = $config['babh']; // 备案编号 - 'W230100103'
        // $this->nonce = md5($now_time . random_int(10000000, 99999999));
        $this->nonce = 'ZZ' . randomChar(30); // 32位随机数
        $this->timestamp = (string)time(); // 10位时间戳
        $this->rsaPrivateKeyFilePath = $config['rsaPrivateKeyFilePath'];
        $this->rsaPublicKeyFilePath = $config['rsaPublicKeyFilePath'];
        if (!array_key_exists('action', $param) || !array_key_exists('data', $param)) {
            throw new ParameterException([
                'message'   => '缺少参数',
                'errorCode' => 999,
            ]);
        }
        $this->action = $param['action']; // 接口id
        // 签名
        $sign_array = [$this->babh, $this->nonce, $this->timestamp, $this->action, $config['appsecret']];
        $sign_str = $this->rsaSign($sign_array);
        $this->sign = $sign_str; // 签名string(500)
        // 数据加密
        $this->data = $this->encrypt($param['data']); // 密文4000
    }

    /**
     * @Desc 主方法
     * @return mixed
     */
    public function appointment()
    {
        $array = [
            'babh'      => $this->babh,
            'nonce'     => $this->nonce,
            'timestamp' => $this->timestamp,
            'action'    => $this->action,
            'sign'      => $this->sign,
            'data'      => $this->data,
        ];
        // soap客户端
        $client = new nusoap_client($this->url, true);
        // 避免乱码
        $client->soap_defencoding = 'UTF-8';
        $client->decode_utf8 = false;
        $client->xml_encoding = 'UTF-8';
        // 调用接口方法
        $result = $client->call('Appointment', ['jsondata' => json_encode($array, JSON_UNESCAPED_SLASHES)]);
        return json_decode($result['AppointmentResult'], true);
    }

    /**
     * @Desc RSA加签
     *
     * @param $params
     *
     * @return string
     */
    private function rsaSign($params)
    {
        return $this->sign($this->getSignContent($params));
    }

    /**
     * @Desc 签名
     *
     * @param $data
     *
     * @return string
     */
    private function sign($data)
    {
        // 打开文件
        $priKey = file_get_contents($this->rsaPrivateKeyFilePath);
        // 提取私钥
        $res = openssl_pkey_get_private($priKey);
        openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
        openssl_free_key($res);
        return base64_encode($sign);
    }

    /**
     * @Desc 排序并转换成字符串
     *
     * @param $params
     *
     * @return string
     */
    private function getSignContent($params)
    {
        sort($params);
        return implode('', $params);
    }

    /**
     * @Desc RSA加密
     *
     * @param $data
     *
     * @return string
     */
    private function encrypt($data)
    {
        //读取公钥文件
        $pubKey = file_get_contents($this->rsaPublicKeyFilePath);
        //转换为openssl格式密钥
        $res = openssl_pkey_get_public($pubKey);
        if (strlen($data) <= 117) {
            openssl_public_encrypt($data, $subCrypted, $res);
            return base64_encode($subCrypted);
        }
        $crypted = [];
        $dataArray = str_split($data, 117);
        foreach ($dataArray as $subData) {
            $subCrypted = null;
            openssl_public_encrypt($subData, $subCrypted, $res);
            $crypted[] = $subCrypted;
        }
        $crypted = implode('', $crypted);
        return base64_encode($crypted);
    }
}